package com.slipper.system.sequence.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.slipper.core.transaction.ReadTransactional;
import com.slipper.core.transaction.WriteTransactional;
import com.slipper.util.Tool;
import com.slipper.system.sequence.bo.SequenceBO;
import com.slipper.system.sequence.dao.SequenceDao;
import com.slipper.util.UUID;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
@ReadTransactional
public class SequenceService {
    @Resource
    private SequenceDao sequenceDao;

    /**
     * 获取当前编号
     */
    public String getNextSequenceById(String id) {
        if (Tool.isBlank(id)) return null;

        SequenceBO sequenceBO = this.sequenceDao.lockById(id);
        String sequence = generateSequence(sequenceBO);
        sequenceBO.setUpdateTime(System.currentTimeMillis());
        this.sequenceDao.update(sequenceBO);
        return sequence;
    }

    private String generateSequence(SequenceBO sequence) {
        String prefix = sequence.getPrefix() == null ? "" : sequence.getPrefix();
        String suffix = sequence.getSuffix() == null ? "" : sequence.getSuffix();
        long curNumber = sequence.getCurrNumber() == null ? 1L : sequence.getCurrNumber();
        Integer totalBit = sequence.getTotalBit();
        prefix = this.changeDate(prefix);
        suffix = this.changeDate(suffix);
        if (!prefix.equals(sequence.getCurPrefix())) {
            sequence.setCurPrefix(prefix);
            curNumber = 1L;
            sequence.setCurrNumber(curNumber);
        }
        StringBuilder sCurNumber = new StringBuilder();
        int len = totalBit - (prefix + suffix + curNumber).length();
        if (len > 0) {
            sCurNumber.append("0".repeat(len));
            sCurNumber.append(curNumber);
        } else {
            sCurNumber = new StringBuilder(Long.toString(curNumber));
        }
        sequence.setCurrNumber(sequence.getCurrNumber() + 1);
        return prefix + sCurNumber + suffix;
    }

    private String changeDate(String tpl) {
        if (tpl == null || tpl.isEmpty()) {
            return "";
        }

        Pattern pattern = Pattern.compile("\\{(.*?(?=}))}");
        Matcher matcher = pattern.matcher(tpl);
        String result = tpl;
        while(matcher.find()) {
            result = result.replace(matcher.group(), parseTime(matcher.group(1)));
        }
        return result;
    }

    private String parseTime(String tpl) {
        if (Tool.isBlank(tpl)) return "";

        Calendar calender = Calendar.getInstance();
        calender.setTime(new Date());
        return tpl.replace("yyyy", "" + calender.get(Calendar.YEAR))
                .replace("yy", Tool.fillLength(calender.get(Calendar.YEAR), 2).substring(2))
                .replace("MM", Tool.fillLength(calender.get(Calendar.MONTH) + 1, 2))
                .replace("month", Tool.fillLength(calender.get(Calendar.MONTH) + 1, 2))
                .replace("dd", Tool.fillLength(calender.get(Calendar.DATE), 2))
                .replace("day", Tool.fillLength(calender.get(Calendar.DATE), 2))
                .replace("HH", Tool.fillLength(calender.get(Calendar.HOUR_OF_DAY), 2))
                .replace("hour", Tool.fillLength(calender.get(Calendar.HOUR_OF_DAY), 2))
                .replace("mm", Tool.fillLength(calender.get(Calendar.MINUTE), 2))
                .replace("minute", Tool.fillLength(calender.get(Calendar.MINUTE), 2))
                .replace("ss", Tool.fillLength(calender.get(Calendar.SECOND), 2))
                .replace("second", Tool.fillLength(calender.get(Calendar.SECOND), 2));
    }
    
    public SequenceBO queryBy(String id) {
        return sequenceDao.queryBy(id);
    }
    
    public List<SequenceBO> queryList(SequenceBO bo) {
        return sequenceDao.queryList(bo);
    }
    
    public PageInfo<SequenceBO> queryPage(SequenceBO bo) {
        PageHelper.startPage(bo.getPageNumber(), bo.getPageSize());
        return new PageInfo<>(sequenceDao.queryList(bo));
    }
    
    @WriteTransactional
    public int save(SequenceBO bo) {
        if (Tool.isNotBlank(bo.getId())) {
            bo.setUpdateTime(System.currentTimeMillis());
            return sequenceDao.update(bo);
        } else {
            bo.setId(UUID.getUUID());
            bo.setUpdateTime(System.currentTimeMillis());
            bo.setCreateTime(System.currentTimeMillis());
            return sequenceDao.insert(bo);
        }
    }

    @WriteTransactional
    public void saveList(List<SequenceBO> sequenceList) {
        if (sequenceList == null) return;

        sequenceList.forEach(sequence -> {
            if (sequenceDao.queryBy(sequence.getId()) != null) {
                sequence.setUpdateTime(System.currentTimeMillis());
                sequenceDao.update(sequence);
            } else {
                sequence.setUpdateTime(System.currentTimeMillis());
                sequence.setCreateTime(System.currentTimeMillis());
                sequenceDao.insert(sequence);
            }
        });
    }

    @WriteTransactional
    public int delete(SequenceBO bo) {
        return sequenceDao.delete(bo);
    }

}
